#include"PageCache.hpp"

PageCache PageCache::_sInc;
Span* PageCache::NewSpan(size_t N)
{
    assert(N > 0 && N < NPAGES);
    if(!_spanLists[N].Empty())
    {
        Span* kSpan = _spanLists[N].PopFront();
        //建立页号与span的映射，方便central cache回收小块内存时查找对应的span
		for (PAGE_ID i = 0; i < kSpan->_n; i++)
		{
			_idSpanMap[kSpan->_pageId + i] = kSpan;
		}
        return kSpan;
    }
//走到这里说明pagecahce中对应下标的哈希桶中没有span，需要往下遍历哈希桶
    for(int i=N+1;i<NPAGES;i++)
    {
        if(!_spanLists[i].Empty())
        {
            Span* nspan=_spanLists[i].PopFront();
            Span* kspan=new Span;

            kspan->_pageId=nspan->_pageId;
            kspan->_n=N;

            nspan->_pageId+=N;
            nspan->_n-=N;

            _spanLists[nspan->_n].PushFront(nspan);
            //存储nSpan的首尾页号与nSpan之间的映射，方便page cache合并span时进行前后页的查找
			_idSpanMap[nspan->_pageId] = nspan;
			_idSpanMap[nspan->_pageId + nspan->_n - 1] = nspan;


            //建立页号与span的映射，方便central cache回收小块内存时查找对应的span
			for (PAGE_ID i = 0; i < kspan->_n; i++)
			{
				_idSpanMap[kspan->_pageId + i] = kspan;
			}

            return kspan;
        }
    }
    //走到这里说明还没分配内存
    //找内存要128页
    Span* bigspan=new Span;
    void* ptr=SystemAlloc(NPAGES-1);
    bigspan->_pageId=(PAGE_ID)ptr>>PAGE_SHIFT;
    bigspan->_n=NPAGES-1;

    _spanLists[bigspan->_n].PushFront(bigspan);
    //复用
    return NewSpan(N);

}

//获取从对象到span的映射
Span* PageCache::MapObjectToSpan(void* obj)
{
	PAGE_ID id = (PAGE_ID)obj >> PAGE_SHIFT; //页号
	auto ret = _idSpanMap.find(id);
	if (ret != _idSpanMap.end())
	{
		return ret->second;
	}
	else
	{
		assert(false);
		return nullptr;
	}
}

void PageCache::ReleaseSpanToPageCache(Span* span)
{

    //对span的前后页，尝试进行合并，缓解内存碎片问题
	//1、向前合并
	while (1)
	{
		PAGE_ID prevId = span->_pageId - 1;
		auto ret = _idSpanMap.find(prevId);
		//前面的页号没有（还未向系统申请），停止向前合并
		if (ret == _idSpanMap.end())
		{
			break;
		}
		//前面的页号对应的span正在被使用，停止向前合并
		Span* prevSpan = ret->second;
		if (prevSpan->_isUse == true)
		{
			break;
		}
		//合并出超过128页的span无法进行管理，停止向前合并
		if (prevSpan->_n + span->_n > NPAGES - 1)
		{
			break;
		}
		//进行向前合并
		span->_pageId = prevSpan->_pageId;
		span->_n += prevSpan->_n;

		//将prevSpan从对应的双链表中移除
		_spanLists[prevSpan->_n].Erase(prevSpan);

		delete prevSpan;
	}
	//2、向后合并
	while (1)
	{
		PAGE_ID nextId = span->_pageId + span->_n;
		auto ret = _idSpanMap.find(nextId);
		//后面的页号没有（还未向系统申请），停止向后合并
		if (ret == _idSpanMap.end())
		{
			break;
		}
		//后面的页号对应的span正在被使用，停止向后合并
		Span* nextSpan = ret->second;
		if (nextSpan->_isUse == true)
		{
			break;
		}
		//合并出超过128页的span无法进行管理，停止向后合并
		if (nextSpan->_n + span->_n > NPAGES - 1)
		{
			break;
		}
		//进行向后合并
		span->_n += nextSpan->_n;

		//将nextSpan从对应的双链表中移除
		_spanLists[nextSpan->_n].Erase(nextSpan);

		delete nextSpan;
	}
	//将合并后的span挂到对应的双链表当中
	_spanLists[span->_n].PushFront(span);
	//建立该span与其首尾页的映射
	_idSpanMap[span->_pageId] = span;
	_idSpanMap[span->_pageId + span->_n - 1] = span;
	//将该span设置为未被使用的状态
	span->_isUse = false;

}